package guyubao.com.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import guyubao.com.entity.UserDetailss;
import guyubao.com.entity.Users;
import guyubao.com.service.RedisService;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * jwt类
 * @author wxa
 */
@Component
public class JwtUtils {
    //Jwt秘钥
    @Value("${spring.security.jwt.key}")
    private String key;
    //过期天数
    @Value("${spring.security.jwt.expire}")
    private int expire;
    //为用户生成Jwt令牌的冷却时间，防止刷接口频繁登录生成令牌，以秒为单位
    @Value("${spring.security.jwt.limit.base}")
    private int limit_base;
    //用户如果继续恶意刷令牌，更严厉的封禁时间(秒)
    @Value("${spring.security.jwt.limit.upgrade}")
    private int limit_upgrade;
    //判定用户在冷却时间内，继续恶意刷令牌的次数(秒)
    @Value("${spring.security.jwt.limit.frequency}")
    private int limit_frequency;
    @Resource
    RedisService redisService;
    @Resource
    FlowUtils utils;

    //根据用户信息创建Jwt令牌
    public  String createJwt(UserDetails user,String id,String username,String email,String role){
        if(this.frequencyCheck(id)) {
            Algorithm algorithm = Algorithm.HMAC256(key);
            // 获得过期时间
            Date expireTime = this.expireTime();
            // 为每个令牌生成唯一id
            String uuid = UUID.randomUUID().toString();
            // 创建令牌
            String token = JWT.create()
                    .withJWTId(uuid)
                    .withClaim("id", id)
                    .withClaim("name", username)
                    .withClaim("email", email)
                    .withClaim("role", role)
                    .withClaim("authorities", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                    .withExpiresAt(expireTime)  //设置过期时间
                    .withIssuedAt(new Date())    //设置创建创建时间
                    .sign(algorithm); // 生成签名
            // 新的令牌设置到白名单,旧令牌就没有,！如果返回值不为空就说明参数不正确
            redisService.addAccount(id,uuid,expire);
            return token;
        }else{
            return null;
        }
    }
    // 获得过期时间
    public Date expireTime(){
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, 3600 * 24 * expire);
        return calendar.getTime();
    }

    /**
     * 校验并转换请求头中的Token令牌
     * @param headerToken 请求头中的Token
     * @return 转换后的令牌
     */
    private String convertToken(String headerToken){
        if (headerToken == null || !headerToken.startsWith("Bearer "))return null;
        return  headerToken.substring(7);
    }

    //根据Jwt验证返回jwt里信息
    public DecodedJWT resolveJwt(String headerToken){
        String token = this.convertToken(headerToken);
        if (token == null) return null;
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT jwt = jwtVerifier.verify(token);  //对JWT令牌进行验证，看看是否被修改
            Date expiresAt = jwt.getExpiresAt(); //获得过期日期
            // 如果时间过期了
            if (new Date().after(expiresAt))
                return null;
            String id = toId(jwt);
            String uuid = toUUID(token);

            String redisToken = redisService.getValue(Const.JWT_WHITE_LIST + id);
            // 如果redis没有该用户,或者是旧的账户
            if (redisToken==null||!redisToken.equals(uuid))
                return null;
            return jwt;
        } catch (JWTVerificationException e) { // 如果jwt被修改会出现运行时异常需要捕获
            return null;
        }
    }

    // jwt信息封装成security里的UserDetails
    public UserDetails toUser(DecodedJWT jwt){
        Map<String, Claim> claims = jwt.getClaims();
        return User
                .withUsername(claims.get("name").asString())
                .password("")
                .authorities(claims.get("authorities").asArray(String.class))
                .build();
    }
    public UserDetailss toUsers(DecodedJWT jwt,String token){
        Map<String, Claim> claims = jwt.getClaims();
        JSONObject jsonObject = JSON.parseObject(Base64.getDecoder().decode(token.split("\\.")[1].getBytes(StandardCharsets.UTF_8)));
        return (UserDetailss) Users
                .withUsername(claims.get("name").asString())
                .id(toId(jwt))
                .name(jsonObject.get("name").toString())
                .email(jsonObject.get("email").toString())
                .role(jsonObject.get("role").toString())
                .password("")
                .authorities(claims.get("authorities").asArray(String.class))
                .build();
    }

    // 获得jwt参数里的用户id
    public String toId(DecodedJWT jwt){
        return jwt.getClaim("id").asString();
    }
    // 获得jwt参数里的身份
    public String toRole(String jwt){
        try{
            return (String) JSON.parseObject(Base64.getDecoder().decode(jwt.split("\\.")[1].getBytes(StandardCharsets.UTF_8))).get("role");
        }catch (JWTDecodeException e){
            return null;
        }
    }
    // 获得jwt参数里的jti(uuid)
    public String toUUID(String jwt){
        try{
            return (String) JSON.parseObject(Base64.getDecoder().decode(jwt.split("\\.")[1].getBytes(StandardCharsets.UTF_8))).get("jti");
        }catch (JWTDecodeException e){
            return null;
        }
    }
    // 获得jwt参数里的email
    public String toEmail(String jwt){
        try{
            return (String) JSON.parseObject(Base64.getDecoder().decode(jwt.split("\\.")[1].getBytes(StandardCharsets.UTF_8))).get("email");
        }catch (JWTDecodeException e){
            return null;
        }
    }

    /***
     * 删除白名单里的jwt
     * @param headerToken 请求头中携带的令牌
     * @return 是否操作成功
     */
    public Boolean deleteJwt(String headerToken){
        String token = this.convertToken(headerToken);
        if (token == null) {
            return false;
        }
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            //对JWT令牌进行验证，看看是否被修改
            DecodedJWT jwt = jwtVerifier.verify(token);
            String id = toId(jwt);
            return redisService.delKey(Const.JWT_WHITE_LIST + id);
        } catch (JWTVerificationException e) { // 如果jwt被修改会出现运行时异常需要捕获
            return false;
        }
    }
    /**
     * 频率检测，防止用户高频申请Jwt令牌，并且采用阶段封禁机制
     * 如果已经提示无法登录的情况下用户还在刷，那么就封禁更长时间
     * @param id 用户ID
     * @return 是否通过频率检测
     */
    private boolean frequencyCheck(String id){
        String key = Const.JWT_FREQUENCY + id;
        return utils.limitOnceUpgradeCheck(key, limit_frequency, limit_base, limit_upgrade);
    }
}